#pragma once
#include <vector>
#include "user_table.hpp"
#include "online_manager.hpp"
#define BOARD_ROW 15
#define BOARD_COL 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2
typedef enum
{
    GAME_START,
    GAME_OVER
} room_statu;

class room
{
public:
    room(uint64_t room_id, user_table *tb_user, online_manager *online_user)
        : _room_id(room_id), _statu(GAME_START), _player_count(0),
          _tb_user(tb_user), _online_user(online_user),
          _board(BOARD_ROW, std::vector<int>(BOARD_COL, 0))
    {
        DBG_LOG("%lu 房间创建成功!", _room_id);
    }
    ~room()
    {
        DBG_LOG("%lu 房间销毁成功!", _room_id);
    }
    uint64_t id() { return _room_id; }
    room_statu statu() { return _statu; }
    int player_count() { return _player_count; }
    void add_white_user(uint64_t uid)
    {
        _white_id = uid;
        _player_count++;
    }
    void add_black_user(uint64_t uid)
    {
        _black_id = uid;
        _player_count++;
    }
    uint64_t get_white_user() { return _white_id; }
    uint64_t get_black_user() { return _black_id; }

    // 处理下棋
    Json::Value handle_chess(Json::Value &req)
    {
        Json::Value json_resp = req;
        // 判断房间中两个玩家是否在线,任意一个不在线,则另一方胜利
        int chess_row = req["row"].asInt();
        int chess_col = req["col"].asInt();
        uint64_t cur_uid = req["uid"].asUInt64();
        if (_online_user->in_game_room(_white_id) == false)
        {
            json_resp["result"] = true;
            json_resp["reson"] = "对方已离线,您获得胜利!";
            json_resp["winner"] = (Json::UInt64)_black_id;
            return json_resp;
        }
        if(_online_user->in_game_room(_black_id) == false)
        {
            json_resp["result"] = true;
            json_resp["reson"] = "对方已离线,您获得胜利!";
            json_resp["winner"] = (Json::UInt64)_white_id;
            return json_resp;
        }
        //获取走棋位置,判断当前走棋是否合理(位置是否已经被占用)
        if(_board[chess_row][chess_col] != 0)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "当前位置已有其他棋子!";
            return json_resp;
        }
        int cur_color = cur_uid == _white_id ? CHESS_WHITE : CHESS_BLACK;
        _board[chess_row][chess_col] = cur_color;
        //判断是否有玩家胜利(从当前走棋位置开始判断是否存在五子相连)
        uint64_t winner_id = check_win(chess_row, chess_col, cur_color);
        if(winner_id != 0)
        {
            json_resp["reason"] = "五子相连,你赢了!";
        }
        json_resp["result"] = true;
        json_resp["winner"] = (Json::UInt64)winner_id;
        return json_resp;
    }
    //处理聊天
    Json::Value handle_chat(Json::Value &req,uint64_t curid)
    {
        Json::Value json_resp = req;
        //检测消息中是否包含敏感词
        std::string msg = req["message"].asString();
        //TODO 可用数组添加敏感词检测
        size_t pos = msg.find("垃圾");
        if(pos != std::string::npos)
        {
            json_resp["uid"] = (Json::UInt64)curid;
            json_resp["result"] = false;
            json_resp["reason"] = "消息包含敏感词,发送失败!";
            return json_resp;
        }
        //广播返回消息
        json_resp["result"] = true;
        return json_resp;
    }
    //处理玩家退出房间
    void handle_exit(uint64_t uid)
    {
        //若下棋中退出,则对方胜利,若下棋结束退出,则是正常退出
        Json::Value json_resp;
        if(_statu == GAME_START)
        {
            uint64_t winner_id = (Json::UInt64)(uid == _white_id ? _black_id : _white_id);
            json_resp["optype"] = "put_chess";
            json_resp["result"] = true;
            json_resp["reason"] = "对方掉线,你赢了!";
            json_resp["room_id"] = (Json::UInt64)_room_id;
            json_resp["uid"] = (Json::UInt64)uid;
            json_resp["row"] = -1;
            json_resp["col"] = -1;
            json_resp["winner"] = (Json::UInt64)winner_id;
            uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
            _tb_user->win(winner_id);
            _tb_user->lose(loser_id);
            _statu = GAME_OVER;
            broadcast(json_resp);
        }
        _player_count--;
        return;
    }
    //总的请求处理函数,在函数内部,区分请求类型,
    //根据不同的请求调用不同的处理函,得到响应进行广播
    void handle_request(Json::Value &req,uint64_t curid)
    {
        //1.校验房间号是否匹配
        Json::Value json_resp;
        uint64_t room_id = req["room_id"].asUInt64();
        if(room_id != _room_id)
        {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "房间号不匹配!";
            return broadcast(json_resp);
        }
        //根据不同的请求类型调用不同的处理函数
        if(req["optype"].asString() == "put_chess")
        {
            json_resp = handle_chess(req);
            if(json_resp["winner"].asUInt64() != 0)
            {
                uint64_t winner_id = json_resp["winner"].asUInt64();
                uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
                _tb_user->win(winner_id);
                _tb_user->lose(loser_id);
                _statu = GAME_OVER;
            }
        }
        else if(req["optype"].asString() == "chat")
        {
            json_resp = handle_chat(req,curid);
        }
        else
        {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "未知请求类型";
        }
        std::string body;
        json_util::serialize(json_resp, body);
        DBG_LOG("%lu房间-广播动作:%s", _room_id, body.c_str());
        return broadcast(json_resp);
    }
    //将指定的信息广播给房间中所有玩家
    void broadcast(Json::Value &rsp)
    {
        //1.对要响应的信息进行序列化
        std::string body;
        json_util::serialize(rsp, body);
        //2.获取房间中所有用户的通信连接
        //3.发送响应信息
        bool only_white_chat = rsp["uid"].asUInt64() == _white_id && rsp["reason"].asString() == std::string("消息包含敏感词,发送失败!");
        bool only_black_chat = rsp["uid"].asUInt64() == _black_id && rsp["reason"].asString() == std::string("消息包含敏感词,发送失败!");
        websocket_server::connection_ptr wconn;
        bool ret = _online_user->get_conn_from_game_room(_white_id, wconn);
        if(ret)
        {
            if(!only_black_chat)
            {
                wconn->send(body);
            }
        }
        else
        {
            DBG_LOG("%lu房间-白棋玩家连接获取失败", _room_id);
        }
        websocket_server::connection_ptr bconn;
        ret = _online_user->get_conn_from_game_room(_black_id, bconn);
        if(ret)
        {
            if(!only_white_chat)
            {
                bconn->send(body);
            }
        }
        else
        {
            DBG_LOG("%lu房间-黑棋玩家连接获取失败", _room_id);
        }
        return;
    }

private:
    uint64_t _room_id;
    room_statu _statu;
    int _player_count;
    uint64_t _white_id;
    uint64_t _black_id;
    user_table *_tb_user;
    online_manager *_online_user;
    std::vector<std::vector<int>> _board;

private:
    //判断是否五子相连
    bool five(int row, int col, int row_off, int col_off, int color)
    {
        // row和col是下棋位置 row_off和col_off是偏移量,也是方向
        int count = 1;
        int search_row = row + row_off;
        int search_col = col + col_off;
        while (search_row >= 0 && search_row < BOARD_ROW &&
               search_col >= 0 && search_col < BOARD_COL &&
               _board[search_row][search_col] == color)
        {
            // 同色棋子数量++
            count++;
            // 检索位置后继续向后偏移
            search_row += row_off;
            search_col += col_off;
        }
        search_row = row - row_off;
        search_col = col - col_off;
        while (search_row >= 0 && search_row < BOARD_ROW &&
               search_col >= 0 && search_col < BOARD_COL &&
               _board[search_row][search_col] == color)
        {
            // 同色棋子数量++
            count++;
            // 检索位置后继续向后偏移
            search_row -= row_off;
            search_col -= col_off;
        }
        return (count >= 5);
    }
    uint64_t check_win(int row, int col, int color)
    {
        // 从下棋位置的四个 不同方向检测是否出现了5个及以上的相同颜色棋子
        // 横行 纵列 正斜线 反斜线
        if (five(row, col, 0, 1, color) ||
            five(row, col, 1, 0, color) ||
            five(row, col, -1, 1, color) ||
            five(row, col, -1, -1, color))
        {
            // 任意方向出现了true也就是五子相连,则设置返回值
            return color == CHESS_WHITE ? _white_id : _black_id;
        }
        return 0;
    }
};